package com.zengjianjun.base.log.aop;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.zengjianjun.base.log.properties.BaseLogProperties;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求日志
 *
 * @author zengjianjun
 */
@Slf4j
@Component
@Aspect
public class LogAspect {

    @Resource
    private BaseLogProperties baseLogProperties;
    /**
     * 定义一个切点
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) " +
            "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping)")
    public void controllerPointcut() {
    }

    @Before("controllerPointcut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        if (baseLogProperties.isEnableLongId()) {
            // 增加日志流水号
            MDC.put("LOG_ID", String.valueOf(IdUtil.fastSimpleUUID()));
        }
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes, "");
        HttpServletRequest request = attributes.getRequest();
        // 打印请求信息
        log.info("------------- 开始 -------------");
        log.info("请求地址: [{} {} {}]", request.getRemoteAddr(), request.getMethod(), request.getRequestURL().toString());
        // 打印请求参数
        log.info("请求参数: {}", this.getRequestParams(joinPoint));
    }

    @Around("controllerPointcut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //执行前，打印开始时间
        long startTime = System.currentTimeMillis();
        //执行业务内容
        Object result = proceedingJoinPoint.proceed();
        //执行后
        log.info("返回结果: {}", JSON.toJSONString(result));
        log.info("------------- 结束 耗时：{} ms -------------", System.currentTimeMillis() - startTime);
        return result;
    }

    private String getRequestParams(JoinPoint proceedingJoinPoint) {
        try {
            // 参数名
            String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
            // 参数值
            Object[] paramValues = proceedingJoinPoint.getArgs();
            return new JSONObject(this.getStringObjectMap(paramNames, paramValues)).toJSONString();
        } catch (Exception e) {
            return "获取请求参数失败：" + e.getMessage();
        }
    }

    private Map<String, Object> getStringObjectMap(String[] paramNames, Object[] paramValues) {
        Map<String, Object> requestParams = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];
            if (value instanceof HttpServletRequest || value instanceof HttpServletResponse) {
                continue;
            }
            // 如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                // 获取文件名
                value = file.getOriginalFilename();
            }
            requestParams.put(paramNames[i], value);
        }
        return requestParams;
    }
}
